var DEFINITION_FILE_EXT = 'ts';

var DEFINITION_INTERFACE_DEFINE = `
export abstract class Definition {
  page?: number;
  limit?: number;
  
  [key: string]: any;

  initWithObject(fields: any) {
    Object.keys(fields).forEach(key=> {
        let value = fields[key];
        this[key] = value;
      });
  }

  toObject() {
    var obj: any = {};
    Object.keys(this).forEach(key=> {
      let value = this[key];
      obj[key] = value;
    });
    return obj;
  }
}
`;

var RESULT_INTEFACE_DEFINE = `
import { Definition } from './Definition';

export abstract class Result<T extends Definition> {
  code?: number;
  message?: string;
  data?: T;
}

export abstract class ResultList<T extends Definition> {
  code?: number;
  message?: string;
  data?: T[]
}
`;

function definitionClassTmpl(CLASS_NAME, CLASS_REFS, PROPERTIES) {
  return `
import { Definition } from './Definition';
${CLASS_REFS}
export class ${CLASS_NAME} extends Definition {
  ${PROPERTIES}
}
  `;
}

function definitionClassRefsTmpl(REFS) {
  return REFS.map((ref) => {
    if (!ref) return '';
    var list = ref.split('/');
    var className = list[list.length - 1];
    return `import { ${className} } from './${ref}';\n`;
  }).join('');
}

function definitionPropertiesTmpl(definition) {
  return `
${(() => {
  if(!definition.properties) return "";
  return Object.keys(definition.properties)
    .map((propertyName) => {
      var property = definition.properties[propertyName];
      if (property.$ref) {
        var className = definition.findRef(property.$ref).className;
        return `\t${className} ${propertyName};\n`;
      }
      var desc = property.description ? `\t/// ${property.description}\n` : '';
      if (property.enum) {
        desc += `\t/// 枚举值: ${property.enum.toString()}\n`;
      }
      switch (property.type) {
        case 'string':
          return `${desc}\t${propertyName}?: string;\n`;
        case 'integer':
          return `${desc}\t${propertyName}?: number;\n`;
        case 'float':
        case 'number':
          return `${desc}\t${propertyName}?: number;\n`;
        case 'array':
          if (property.items.$ref) {
            console.log(property.items.$ref, definition.findRef(property.items.$ref));
          }
          var className = property.items.$ref
            ? definition.findRef(property.items.$ref).className
            : 'any';
          return `${desc}\t${propertyName}?: ${className}[];\n`;
        default:
          return `${desc}\t${propertyName}?: any;\n`;
      }
    })
    .join('');
})()}
  `;
}

function classTmpl(ClassName, ClassMethods, REFS) {
  return `
import request from '@/utils/request';
${REFS}

export class ${ClassName} {
${ClassMethods}
}`;
}

function methodTmpl(api) {
  var OperationId = api.operationId.replace(/\_\d+$/, '');
  var Summary = api.summary;
  var Path = api.path;
  var Method = api.method;
  var UpperMethod = Method.toUpperCase();

  var paramsRegex = /(\{\w+\})/gm;
  var params = Path.match(paramsRegex) || [];
  var _url = '/v1' + Path;
  var pathUrl = '/v1' + Path;
  var paramsArgs = [];
  for (var i = 0; i < params.length; i++) {
    var param = params[i].replace(/[\{\}]/g, '');
    _url = _url.replace(params[i], '${' + param + '}');
    paramsArgs.push(`${param}: string, `);
  }

  var queryArgs = [];
  var queryStrArray = [];
  if (api.queryParams) {
    for (var i = 0; i < api.queryParams.length; i++) {
      var param = api.queryParams[i];
      var paramName = param.name;
      queryStrArray.push(`${paramName}=\$\{${paramName} ?? "" \}`);
      switch (param.format) {
        case 'string':
          queryArgs.push(`${paramName}: string, `);
          break;
        case 'integer':
          queryArgs.push(`${paramName}: number, `);
          break;
        case 'float':
          queryArgs.push(`${paramName}: number, `);
          break;
        default:
          queryArgs.push(`${paramName}: any, `);
          break;
      }
    }
    if (queryStrArray.length > 0) {
      _url += '?' + queryStrArray.join('&');
    }
  }

  if (Method == 'post' || Method == 'put') {
    var ResponseParam = (api.parseResponseTs && api.parseResponseTs()) || 'any';
    var ParseBodyParam = api.parseBodyParamTs && api.parseBodyParamTs();
    return `
    static ${OperationId}Path: string = "${pathUrl}";
    /// ${Summary}
    async ${OperationId}(${paramsArgs.join('')}${queryArgs.join('')}${
      ParseBodyParam ?? ''
    } headers?: any):  Promise<${ResponseParam}> {
      return request(\`${_url}\`, {
        method: "${UpperMethod}",
        data: ${!ParseBodyParam ? 'null' : 'JSON.stringify(data)'} ,
        headers: headers,
      }) as Promise<any>;
    }
    `;
  }

  if (Method == 'get' || Method == 'delete') {
    var ResponseParam = (api.parseResponseTs && api.parseResponseTs()) || 'any';
    return `
    static ${OperationId}Path: string = "${pathUrl}";

    async ${OperationId}(${paramsArgs.join('')}${queryArgs.join(
      '',
    )} headers?: any) : Promise<${ResponseParam}> {
      return request(\`${_url}\`, {
        method: "${UpperMethod}",
        headers: headers,
      }) as Promise<any>;
    }
    `;
  }

  return '';
}

function resultClassTmpl(className, extendsClassName, refClassName) {
  var originRefClassName;
  if (extendsClassName.includes('Result<List')) {
    extendsClassName = extendsClassName.replace('Result<List', 'ResultList<');
    var matches = /^ResultList\<(.+)\>$/g.exec(extendsClassName);
    originRefClassName = matches[1];
  } else {
    var matches = /^Result\<(.+)\>$/g.exec(extendsClassName);
    originRefClassName = matches[1];
  }

  return `
import { Result, ResultList } from './Result';
import { ${refClassName} } from './${refClassName}';

export class ${className} extends ${extendsClassName.replace(originRefClassName, refClassName)} {

}
`;
}

module.exports = {
  DEFINITION_FILE_EXT: DEFINITION_FILE_EXT,
  DEFINITION_INTERFACE_DEFINE: DEFINITION_INTERFACE_DEFINE,
  RESULT_INTEFACE_DEFINE: RESULT_INTEFACE_DEFINE,
  definitionClassTmpl: definitionClassTmpl,
  definitionPropertiesTmpl: definitionPropertiesTmpl,
  definitionClassRefsTmpl: definitionClassRefsTmpl,
  classTmpl: classTmpl,
  resultClassTmpl: resultClassTmpl,
  methodTmpl: methodTmpl,
};
